/*
 * Improbability Drive
 * Phillip Cardon, Thach Nguyen, Cristopher Claeys
 * 4/26/2011
 */

package ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

import structures.Reporter;

public class CrawlerWindow extends JFrame {

	/**
	 * Serialized ID.
	 */
	private static final long serialVersionUID = 8005118206334505664L;
	
	/**
	 * Number of keywords to search for.
	 */
	private static final int NUM_KEYWORDS = 10;
	
	/**
	 * Default number of pages to crawl.
	 */
	private static final int DEFAULT_NUM_PAGES = 15;
	
	private static final int DEFAULT_THREAD = 4;
	
	/**
	 * MAX number of pages to crawl.
	 */
	private static final int MAX_NUM_PAGES = 15000;
	
	/**
	 * Number of pages to crawl input.
	 */
	private final IntTextField numPagesInput;
	
	private final IntTextField numParserInput;
	
	private final IntTextField numRetrieveInput;
	
	/**
	 * List of test fields to accept input.
	 */
	private final List<JTextField> fields;
	
	/**
	 * Threadedness, fun word.
	 */
	private boolean multiTreaded = true;
	
	/**
	 * Seed box.
	 */
	
	private final JTextField seedBox;
	
	/**
	 * Reporter
	 */
	private Reporter report;
	
	/**
	 * Reporter Window
	 */
	private ReportWindow rwin;
	
	/**
	 * Constructor
	 * @param startTime
	 */
	
	public CrawlerWindow () {
		super();
		fields = new ArrayList<JTextField>();
		numPagesInput = new IntTextField(DEFAULT_NUM_PAGES, 6);
		numParserInput = new IntTextField(DEFAULT_THREAD, 6);
		numRetrieveInput = new IntTextField(DEFAULT_THREAD, 6);
		report = null;
		seedBox = new JTextField("http://faculty.washington.edu/gmobus/");
	}
	
	public void start() {
		
		buildUI();
		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		this.pack();
		setVisible(true);
	}
	
	public void dispose() {
		super.dispose();
		System.exit(0);
	}

	private void buildUI() {
		this.setTitle("Improbability Drive Web Crawler");
		this.setLayout(new BorderLayout());
		this.setResizable(false);
		
		JPanel center = keywordSection();
		pages(center);
		addThreadNumbers(center);
		threadSection(center);
		addSeedBox(center);
		makeButton();
		
	}
	
	private void addThreadNumbers(JPanel center) {
		JLabel retrieve = new JLabel("Retriever threads:");
		retrieve.setHorizontalAlignment(SwingConstants.CENTER);
		center.add(retrieve);
		center.add(numRetrieveInput);
		JLabel parse = new JLabel("Parser threads:");
		parse.setHorizontalAlignment(SwingConstants.CENTER);
		center.add(parse);
		center.add(numParserInput);
	}
	
	private void addSeedBox(JPanel center) {
		JLabel seedLabel = new JLabel("Seed URL:");
		center.add(seedLabel);
		center.add(seedBox);
		
	}

	private void makeButton() {
		JButton crawl = new JButton("Crawl");
		this.add(crawl, BorderLayout.SOUTH);
		makeActionListener(crawl);
	}

	private void makeActionListener(JButton crawl) {
		final JFrame temp = this;
		ActionListener launch = new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				rwin = new ReportWindow();
				List<String> keys = new LinkedList<String>();
				for (JTextField box : fields) {
					if (box.getText().length() > 0) {
						keys.add(box.getText());
					}
				}
				
				Launcher.addKeys(keys.toArray(new String[keys.size()]));
				URL seed = null;
				try
				{
					seed = new URL(seedBox.getText());
					Launcher.plantSeed(seed);
					if (multiTreaded) {
						report = Launcher.multi(numPagesInput.getValue(), numRetrieveInput.getValue(), 
								numParserInput.getValue());
						
					} else {
						report = Launcher.single(numPagesInput.getValue());
					}
				} catch (MalformedURLException e) {
					JOptionPane.showMessageDialog(temp,  seedBox.getText() +
							" \nis not a valid URL", "Invalid URL", JOptionPane.ERROR_MESSAGE);
				}
				report.addObserver(rwin);
				rwin.setStart(System.currentTimeMillis());
				rwin.pack();
				rwin.setVisible(true);
				
			}
			
		};
		crawl.addActionListener(launch);
		
	}

	private void pages(JPanel center) {
		JLabel crawlPages = new JLabel("Number of pages to crawl:");
		crawlPages.setHorizontalAlignment(SwingConstants.CENTER);
		center.add(crawlPages);
		center.add(numPagesInput);
	}

	private JPanel keywordSection () {
		// make and add text fields to UI 
		JLabel keywords = new JLabel("Keywords:"); // Label for all textfields
		
		JPanel center = new JPanel(); // panel to contain keywords and radiobuttons
		center.setLayout(new BoxLayout(center, BoxLayout.Y_AXIS));
		keywords.setHorizontalTextPosition(SwingConstants.CENTER);
		keywords.setHorizontalAlignment(SwingConstants.CENTER);
		JSeparator first = new JSeparator(SwingConstants.HORIZONTAL); //seperator for title
		first.setPreferredSize(new Dimension(25, 1));
		center.add(keywords);
		center.add(first); //seperator
		this.add(center, BorderLayout.CENTER); //add center container to frame
		center.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); // make things look pretty.
		JPanel keywordContainer = new JPanel(); // Container for text boxes.
		keywordContainer.setLayout(new BoxLayout(keywordContainer, BoxLayout.Y_AXIS)); //set layout
		keywordContainer.setBorder(BorderFactory.createEmptyBorder(7, 0, 7, 0)); //make it pretty
		//make text boxes and add to container
		for (int i = 0; i < NUM_KEYWORDS; i++) {
			JTextField keywordBox = new JTextField();
			fields.add(keywordBox);
			keywordContainer.add(keywordBox);
		}
		center.add(keywordContainer); //add textbox container to main container
		return center;
	}
	
	private void threadSection (JPanel center) {
		// make Multithreaded /Single Thread radio buttons
		JSeparator second = new JSeparator(SwingConstants.HORIZONTAL);
		second.setPreferredSize(new Dimension(25, 7));
		center.add(second);
		JLabel threads = new JLabel("Thread Selection:");
		threads.setHorizontalAlignment(SwingConstants.CENTER);
		center.add(threads);
		ButtonGroup threadSelection = new ButtonGroup();
		JRadioButton single = new JRadioButton("Single");
		JRadioButton multi = new JRadioButton("Multi");
		threadSelection.add(single);
		threadSelection.add(multi);
		multi.setSelected(true);
		
		single.addActionListener(new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				multiTreaded = false;
			}
		});
		
		multi.addActionListener(new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				multiTreaded = true;
			}
		});
		center.add(single);
		center.add(multi);
	}

	/**
	 * IntTextField
	 * Retrieved from:
	 * http://www.java2s.com/Code/Java/Swing-JFC/Textfieldonlyacceptsnumbers.htm
	 */
	class IntTextField extends JTextField {
		/**
		 * Serializeable ID.
		 */
		private static final long serialVersionUID = -2915729584011040209L;

		public IntTextField(int defval, int size) {
		    super("" + defval, size);
		  }

		  @Override
		protected Document createDefaultModel() {
		    return new IntTextDocument();
		  }

		  @Override
		public boolean isValid() {
		    try {
		    	Integer.parseInt(getText());
		    	return true;
		    } catch (NumberFormatException e) {
		    	return false;
		    }
		    /**
		     * ADDED BY PHILLIP CARDON
		     */
		    catch (NullPointerException e) {
		    	return false;
		    }
		  }

		  public int getValue() {
		    try {
		      return Math.abs(Integer.parseInt(getText()));
		    } catch (NumberFormatException e) {
		      return 0;
		    }
		  }
	
		  class IntTextDocument extends PlainDocument {
		    /**
			 * Serializable ID.
			 */
			private static final long serialVersionUID = -6407898627883079389L;

			@Override
			public void insertString(int offs, String str, AttributeSet a)
		        throws BadLocationException {
		      if (str == null)
		        return;
		      String oldString = getText(0, getLength());
		      String newString = oldString.substring(0, offs) + str
		          + oldString.substring(offs);
		      try {
		        Integer.parseInt(newString + "0");
		        super.insertString(offs, str, a);
		      } catch (NumberFormatException e) {
		      }
		    }
		  }
	}
}
